home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1833 / 1833.xpi / modules / yoonoCookies.js < prev    next >
Text File  |  2009-12-16  |  65KB  |  1,727 lines

  1. const EXPORTED_SYMBOLS = ["CookiesService"];
  2.  
  3. // First, start with some various debug functions
  4. const DIRSERVICE = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
  5. var logFile = DIRSERVICE.get('ProfDS', Components.interfaces.nsIFile);
  6. logFile.append("cookies.log");
  7. var fileOutputStream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream);
  8. fileOutputStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0);
  9. var converter = Components.classes['@mozilla.org/intl/converter-output-stream;1'].createInstance(Components.interfaces.nsIConverterOutputStream);
  10. converter.init(fileOutputStream, 'UTF-8', 1024, '-');
  11.  
  12. function log(component,msg) {
  13.  
  14.  return;
  15. /*  
  16.   converter.writeString(component+" - "+msg+"\n");
  17.   converter.flush();
  18.   //fileOutputStream.close();
  19.   
  20.   return;
  21. */  
  22.   if (msg)
  23.     Components.utils.reportError("Cookies::"+component+" - "+msg);
  24.   else
  25.     Components.utils.reportError("Cookies - "+msg);
  26. }
  27.  
  28. function inspect(obj) {
  29.   var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
  30.   var win = wm.getMostRecentWindow("navigator:browser");
  31.   if (!win.inspectObject) return;
  32.   win.inspectObject(obj);
  33. }
  34.  
  35. function alert(m) {
  36.   var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
  37.          .getService(Components.interfaces.nsIAppShellService)
  38.          .hiddenDOMWindow;
  39.   hiddenWindow.alert(m);
  40. }
  41.  
  42.  
  43.  
  44. ///////////////////////////////////////////////////////////////////////////
  45. // Class Cookie
  46. //-------------------------------------------------------------------------
  47. // Represent *one* cookie key/value
  48. // Have two static methods : cookiesListToString and parseCookieDate
  49. //
  50. function Cookie(cookieAttributes) {
  51.   this.name = cookieAttributes.name;
  52.   this.value = cookieAttributes.value;
  53.   
  54.   this.host = cookieAttributes.host;
  55.   this.path = cookieAttributes.path;
  56.   
  57.   this.isSecure = cookieAttributes.isSecure;
  58.   this.isHttpOnly = cookieAttributes.isHttpOnly;
  59.   
  60.   this.expires = cookieAttributes.expires;
  61.   this.maxage = cookieAttributes.maxage;
  62. }
  63.  
  64. Cookie.parseCookieDate = function (s) { // note: static function ...
  65.   return new Date(s.replace(/-/g," "));
  66. }
  67.  
  68. Cookie.cookiesListToString = function (list) {
  69.   var cookieData = "";
  70.   for (var i = 0; i < list.length; ++i) {
  71.     var cookie = list[i];
  72.     
  73.     // check if we have anything to write
  74.     if (cookie.name.length>0 || cookie.value.length>0) {
  75.       // if we've already added a cookie to the return list, append a "; " so
  76.       // that subsequent cookies are delimited in the final list.
  77.       if (cookieData.length>0) {
  78.         cookieData += "; ";
  79.       }
  80.  
  81.       if (cookie.name.length>0) {
  82.         // we have a name and value - write both
  83.         cookieData += cookie.name + "=" + cookie.value;
  84.       } else {
  85.         // just write value
  86.         cookieData += cookie.value;
  87.       }
  88.     }
  89.   }
  90.   return cookieData;
  91. }
  92.  
  93. const mTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
  94.                   .getService(Components.interfaces.nsIEffectiveTLDService);
  95.  
  96. Cookie.prototype._checkDomain = function(uri) {
  97.   // JS equivalent of CheckDomain
  98.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1932
  99.   
  100.   var hostFromURI = uri.asciiHost;
  101.   if (!hostFromURI) return false;
  102.   
  103.   // trim trailing dots
  104.   hostFromURI = hostFromURI.replace(/(^\.)|(\.$)/g,"");
  105.  
  106.   // if a domain is given, check the host has permission
  107.   if (this.host && this.host.length>0) {
  108.     // switch to lowercase now, to avoid case-insensitive compares everywhere
  109.     this.host = this.host.replace(/(^\.)|(\.$)/g,"").toLowerCase();
  110.     
  111.     // get the base domain for the host URI.
  112.     // e.g. for "images.bbc.co.uk", this would be "bbc.co.uk", which
  113.     // represents the lowest level domain a cookie can be set for.
  114.     try {
  115.     var baseDomain = mTLDService.getBaseDomain(uri);
  116.     baseDomain = baseDomain.replace(/(^\.)|(\.$)/g,"");
  117.     } catch(e) {
  118.       // check whether the host is an IP address, and leave the cookie as
  119.       // a non-domain one. this will require an exact host match for the cookie,
  120.       // so we eliminate any chance of IP address funkiness (e.g. the alias 127.1
  121.       // domain-matching 99.54.127.1). bug 105917 originally noted the
  122.       // requirement to deal with IP addresses.
  123.       if (e.name=="NS_ERROR_HOST_IS_IP_ADDRESS") {
  124.         return hostFromURI == this.host;
  125.       }
  126.       log("SetCondition","Ignore cookie on domain 1");
  127.       return false;
  128.     }
  129.  
  130.     // ensure the proposed domain is derived from the base domain; and also
  131.     // that the host domain is derived from the proposed domain (per RFC2109).
  132.     // we prepend a dot before the comparison to ensure e.g.
  133.     // "mybbc.co.uk" isn't matched as a superset of "bbc.co.uk".
  134.     hostFromURI = "."+hostFromURI;
  135.     this.host = "."+this.host;
  136.     baseDomain = "."+baseDomain;
  137.     function StringEndsWith(base, str) {
  138.       return base.length >= str.length && base.lastIndexOf(str) + str.length == base.length
  139.     }
  140.     var accept = StringEndsWith(this.host, baseDomain) &&
  141.            StringEndsWith(hostFromURI, this.host);
  142.     if (!accept)
  143.       log("SetCondition","Ignore cookie on domain 2 : "+this.host+"/"+baseDomain+" -- "+hostFromURI+"/"+this.host);
  144.     return accept;
  145.  
  146.     /*
  147.      * note: RFC2109 section 4.3.2 requires that we check the following:
  148.      * that the portion of host not in domain does not contain a dot.
  149.      * this prevents hosts of the form x.y.co.nz from setting cookies in the
  150.      * entire .co.nz domain. however, it's only a only a partial solution and
  151.      * it breaks sites (IE doesn't enforce it), so we don't perform this check.
  152.      */
  153.   }
  154.  
  155.   // block any URIs without a host that aren't file:/// URIs
  156.   if (!hostFromURI || hostFromURI.length==0) {
  157.     if (!uri.schemeIs("file")) {
  158.       log("SetCondition","Ignore cookie on domain 3");
  159.       return false;
  160.     }
  161.   }
  162.  
  163.   // no domain specified, use hostFromURI
  164.   this.host = hostFromURI;
  165.  
  166.   return true;
  167. }
  168.  
  169. Cookie.prototype._checkPath = function(aHostURI) {
  170.   // JS equivalent of CheckPath
  171.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#2003
  172.   
  173.   // if a path is given, check the host has permission
  174.   if (!this.path || this.path.length==0) {
  175.     log("checkPath","Empty path '"+this.path+"' -> compute automatically!");
  176.     // strip down everything after the last slash to get the path,
  177.     // ignoring slashes in the query string part.
  178.     // if we can QI to nsIURL, that'll take care of the query string portion.
  179.     // otherwise, it's not an nsIURL and can't have a query string, so just find the last slash.
  180.     try {
  181.       aHostURI.QueryInterface(Components.interfaces.nsIURL);
  182.       this.path = aHostURI.directory;
  183.     } catch(e) {
  184.       this.path = aHostURI.path;
  185.       var idx = this.path.lastIndexOf('/');
  186.       if (idx!=-1)
  187.         this.path = this.path.substring(0,idx-1);
  188.     }
  189.   }
  190.  
  191.   if (this.path && this.path.length > 1024 ||
  192.       this.path.indexOf('\t')!=-1 )
  193.     return false;
  194.  
  195.   return true;
  196. }
  197.  
  198. Cookie.prototype.canSetFrom = function (uri) {
  199.   // JS equivalent of SetCookieInternal (part of)
  200.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1385
  201.   
  202.   if (this.name.length + this.value.length >4096) {
  203.     log("SetCondition","cookie too long");
  204.     return false;
  205.   }
  206.   if (this.name.indexOf('\t')!=-1) {
  207.     log("SetCondition","invalid name character : \\t");
  208.     return false;
  209.   }
  210.   
  211.   if (!this._checkDomain(uri)) {
  212.     log("SetCondition","Refuse this domain!!!");
  213.     return false;
  214.   }
  215.   
  216.   if (!this._checkPath(uri)) {
  217.     log("SetCondition","Refuse this path!!!");
  218.     return false;
  219.   }
  220.   
  221.   return true;
  222. }
  223.  
  224. Cookie.prototype.computeExpiracy = function(serverTime) {
  225.   // JS equivalent of GetExpiry
  226.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#2048
  227.   
  228.   // Compute expicary time
  229.   var isSessionCookie=false;
  230.   var delta;
  231.   if (this.maxage && this.maxage.length>0) {
  232.     try {
  233.       delta = parseInt(this.maxage);
  234.     } catch(e){
  235.       log("ComputeExpiracy","maxage not an int");
  236.       isSessionCookie = true;
  237.     }
  238.   } else if (this.expires && this.expires.length>0) {
  239.   
  240.     try {
  241.       var temp = Cookie.parseCookieDate(this.expires);
  242.       //Components.utils.reportError("got expires : "+temp.getTime()+" - "+serverTime);
  243.       delta = (temp.getTime()) - serverTime;
  244.     } catch(e){
  245.       log("ComputeExpiracy","expires not a valid date");
  246.       isSessionCookie = true;
  247.     }
  248.   } else {
  249.     isSessionCookie = true;
  250.   }
  251.   if (delta)
  252.     this.expiry = new Date().getTime()+delta;
  253.   this.isSession = isSessionCookie;
  254. }
  255.  
  256. Cookie.prototype.canGetFrom = function (pathFromURI, isSecure, aHttpBound) {
  257.   // JS equivalent of GetCookieInternal (part of)
  258.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1276
  259.   
  260.   function ispathdelimiter(c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
  261.   
  262.   // if the cookie is secure and the host scheme isn't, we can't send it
  263.   if (this.isSecure && !isSecure) {
  264.     log("GetCondition","Ignore secure : "+this.name);
  265.     return false;
  266.   }
  267.  
  268.   // if the cookie is httpOnly and it's not going directly to the HTTP
  269.   // connection, don't send it
  270.   if (this.isHttpOnly && !aHttpBound) {
  271.     log("GetCondition","Ignore httponly : "+this.name);
  272.     return false;
  273.   }
  274.  
  275.   // calculate cookie path length, excluding trailing '/'
  276.   var cookiePathLen = this.path.length;
  277.   if (cookiePathLen > 0 && this.path[cookiePathLen-1] == '/') {
  278.     --cookiePathLen;
  279.   }
  280.  
  281.   // if the nsIURI path is shorter than the cookie path, don't send it back
  282.   if (pathFromURI.indexOf(this.path.substring(0,cookiePathLen))!=0) {
  283.     log("GetCondition","Ignore on path : "+this.name+" -> "+pathFromURI+" -- "+this.path.substring(0,cookiePathLen));
  284.     return false;
  285.   }
  286.  
  287.   if (pathFromURI.length > cookiePathLen &&
  288.       !ispathdelimiter(pathFromURI[cookiePathLen])) {
  289.     /*
  290.      * |ispathdelimiter| tests four cases: '/', '?', '#', and ';'.
  291.      * '/' is the "standard" case; the '?' test allows a site at host/abc?def
  292.      * to receive a cookie that has a path attribute of abc.  this seems
  293.      * strange but at least one major site (citibank, bug 156725) depends
  294.      * on it.  The test for # and ; are put in to proactively avoid problems
  295.      * with other sites - these are the only other chars allowed in the path.
  296.      */
  297.     log("GetCondition","Ignore on path 2 : "+pathFromURI+"/"+this.path.substring(0,cookiePathLen)+" -- "+pathFromURI[cookiePathLen]);
  298.     return false;
  299.   }
  300.  
  301.   // check if the cookie has expired
  302.   if (this.expiry && this.expiry <= new Date().getTime()) {
  303.     log("GetCondition","Ignore expired : "+this.name);
  304.     return false;
  305.   }
  306.   return true;
  307. }
  308.  
  309.  
  310. ///////////////////////////////////////////////////////////////////////////
  311. // Class CookieStringIterator
  312. //-------------------------------------------------------------------------
  313. // Iterator over a HTTP or JS cookie string 
  314. // in order to return Cookies objects
  315. function CookieStringIterator(str) {
  316.   this._hasNext = true;
  317.   this._str = str;
  318. }
  319. CookieStringIterator.prototype.hasNext = function () {
  320.   return this._hasNext;
  321. }
  322. CookieStringIterator.prototype._getCurrentString = function () {
  323.   return this._str;
  324. }
  325. CookieStringIterator.prototype._setNewString = function (str, hasNext) {
  326.   this._str = str;
  327.   this._hasNext = hasNext;
  328. }
  329. CookieStringIterator._getTokenValue = function(str, startIndex) {
  330.   // JS equivalent of GetTokenValue
  331.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1658
  332.   
  333.   function iswhitespace     (c) { return c == ' '  || c == '\t'; }
  334.   function isterminator     (c) { return c == '\n' || c == '\r'; }
  335.   function isquoteterminator(c) { return isterminator(c) || c == '"'; }
  336.   function isvalueseparator (c) { return isterminator(c) || c == ';'; }
  337.   function istokenseparator (c) { return isvalueseparator(c) || c == '='; }
  338.  
  339.   var tokenString = ""
  340.   var tokenValue = ""
  341.   var lastIndex = str.length-1;
  342.   
  343.   var i = startIndex;
  344.   // find <token>, including any <LWS> between the end-of-token and the
  345.   // token separator. we'll remove trailing <LWS> next
  346.   while (i != lastIndex && iswhitespace(str[i]))
  347.     ++i;
  348.   start = i;
  349.   while (i != lastIndex && !istokenseparator(str[i]))
  350.     ++i;
  351.  
  352.   // remove trailing <LWS>; first check we're not at the beginning
  353.   lastSpace = i;
  354.   if (lastSpace != start) {
  355.     while (--lastSpace != start && iswhitespace(str[lastSpace]));
  356.     ++lastSpace;
  357.   }
  358.   
  359.   //log("Parse","start:"+start+" i:"+i+" lastSpace:"+lastSpace+"("+str[lastSpace]+") lastIndex:"+lastIndex+"("+str[lastIndex]+") lastIndex+1:"+str[lastIndex+1]);
  360.   if (lastSpace==lastIndex && !istokenseparator(str[lastIndex]))
  361.     tokenString = str.substring(start, lastIndex+1);
  362.   else
  363.     tokenString = str.substring(start, lastSpace);
  364.  
  365.   var equalsFound = (str[i] == '=');
  366.   if (equalsFound) {
  367.     //log("Parse","Equals found");
  368.     // find <value>
  369.     while (++i != lastIndex && iswhitespace(str[i]));
  370.  
  371.     start = i;
  372.  
  373.     if (str[i] == '"') {
  374.       //log("Parse","Quote mode");
  375.       // process <quoted-string>
  376.       // (note: cookie terminators, CR | LF, can't happen:
  377.       // they're removed by necko before the header gets here)
  378.       // assume value mangled if no terminating '"', return
  379.       while (++i != lastIndex && !isquoteterminator(str[i])) {
  380.         // if <qdtext> (backwhacked char), skip over it. this allows '\"' in <quoted-string>.
  381.         // we increment once over the backwhack, nullcheck, then continue to the 'while',
  382.         // which increments over the backwhacked char. one exception - we don't allow
  383.         // CR | LF here either (see above about necko)
  384.         if (str[i] == '\\' && (++i == lastIndex || isterminator(str[i])))
  385.           break;
  386.       }
  387.  
  388.       if (i != lastIndex && !isterminator(str[i])) {
  389.         // include terminating quote in attribute string
  390.         tokenValue = str.substring(start, ++i);
  391.         // skip to next ';'
  392.         while (i != lastIndex && !isvalueseparator(str[i]))
  393.           ++i;
  394.       }
  395.     } else {
  396.       
  397.       // process <token-value>
  398.       // just look for ';' to terminate ('=' allowed)
  399.       while (i != lastIndex && !isvalueseparator(str[i]))
  400.         ++i;
  401.       //log("Parse","NewI:"+i+" start:"+start+" lastIndex:"+lastIndex+" str[i]:"+str[i]);
  402.       // remove trailing <LWS>; first check we're not at the beginning
  403.       //if (i != start) {
  404.         lastSpace = i;
  405.         while (--lastSpace != start && iswhitespace(str[lastSpace])) 
  406.           ;
  407.         //log("Parse",i+"-"+lastSpace+"/"+lastIndex+"-"+str[lastSpace]+"/"+str[lastIndex]+"/"+str[lastIndex+1]);
  408.         if (lastSpace+1==lastIndex && !isvalueseparator(str[lastIndex]))
  409.           tokenValue = str.substring(start, lastIndex+1);
  410.         else
  411.           tokenValue = str.substring(start, ++lastSpace);
  412.       //}
  413.     }
  414.   }
  415.   
  416.   var terminator = false;
  417.   // i is on ';', or terminator, or EOS
  418.   if (i != lastIndex) {
  419.     // if on terminator, increment past & return PR_TRUE to process new cookie
  420.     if (isterminator(str[i])) {
  421.       ++i;
  422.       terminator = true;
  423.     } else {
  424.       // fall-through: i is on ';', increment and return PR_FALSE
  425.       ++i;
  426.     }
  427.   }
  428.   var res= {
  429.     tokenString: tokenString, tokenValue: tokenValue,
  430.     newCookie: terminator, newIndex: i,
  431.     equalsFound : equalsFound
  432.   };
  433.   log("getTokenValue", tokenString+" = "+tokenValue);
  434.   //log("getTokenValue","("+str+", "+startIndex+") --> "+res.toSource());
  435.   return res;
  436. }
  437.  
  438. CookieStringIterator.prototype.getNext = function () {
  439.   // JS equivalent of ParseAttributes function
  440.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1743
  441.   
  442.   var str = this._getCurrentString();
  443.   
  444.   var cookieStart = 0;
  445.   var cookieEnd = str.length-1;
  446.   
  447.   var newCookie = false;
  448.   
  449.   var cookieAttributes = {};
  450.   cookieAttributes.isSecure = false;
  451.   cookieAttributes.isHttpOnly = false;
  452.  
  453.   // extract cookie <NAME> & <VALUE> (first attribute), and copy the strings.
  454.   // if we find multiple cookies, return for processing
  455.   // note: if there's no '=', we assume token is <VALUE>. this is required by
  456.   //       some sites (see bug 169091).
  457.   // XXX fix the parser to parse according to <VALUE> grammar for this case
  458.   var result = CookieStringIterator._getTokenValue(str, cookieStart);
  459.   cookieStart = result.newIndex;
  460.   newCookie = result.newCookie;
  461.   if (result.equalsFound) {
  462.     cookieAttributes.name = result.tokenString;
  463.     cookieAttributes.value = result.tokenValue;
  464.   } else {
  465.     cookieAttributes.name = "";
  466.     cookieAttributes.value = result.tokenString;
  467.   }
  468.   
  469.   // extract remaining attributes
  470.   while (cookieStart != cookieEnd && !newCookie) {
  471.     result = CookieStringIterator._getTokenValue(str, cookieStart);
  472.     cookieStart = result.newIndex;
  473.     newCookie = result.newCookie;
  474.  
  475.     if (result.tokenValue && result.tokenValue.length>0) {
  476.       var tempEnd = result.tokenValue.length-1;
  477.       
  478.       if (result.tokenValue[0] == '"' && result.tokenValue[--tempEnd] == '"') {
  479.         // our parameter is a quoted-string; remove quotes for later parsing
  480.         result.tokenValue = result.tokenValue.substring(1,tempEnd);
  481.       }
  482.     }
  483.  
  484.     // decide which attribute we have, and copy the string
  485.     if (result.tokenString.toLowerCase() == "path")
  486.       cookieAttributes.path = result.tokenValue;
  487.  
  488.     else if (result.tokenString.toLowerCase() == "domain")
  489.       cookieAttributes.host = result.tokenValue;
  490.  
  491.     else if (result.tokenString.toLowerCase() == "expires")
  492.       cookieAttributes.expires = result.tokenValue;
  493.  
  494.     else if (result.tokenString.toLowerCase() == "max-age")
  495.       cookieAttributes.maxage = result.tokenValue;
  496.  
  497.     // ignore any tokenValue for isSecure; just set the boolean
  498.     else if (result.tokenString.toLowerCase() == "secure")
  499.       cookieAttributes.isSecure = true;
  500.       
  501.     // ignore any tokenValue for isHttpOnly (see bug 178993);
  502.     // just set the boolean
  503.     else if (result.tokenString.toLowerCase() == "httponly")
  504.       cookieAttributes.isHttpOnly = true;
  505.   }
  506.  
  507.   // rebind aCookieHeader, in case we need to process another cookie
  508.   this._setNewString(str.substring(cookieStart),newCookie);
  509.   
  510.   return new Cookie(cookieAttributes);
  511. }
  512.  
  513.  
  514. ///////////////////////////////////////////////////////////////////////////
  515. // Service CookiesStorage
  516. //-------------------------------------------------------------------------
  517. // Only handle effective storage to a sqlite database
  518. //
  519. var CookiesStorage = {}
  520. CookiesStorage._db = null;
  521. CookiesStorage._openDB = function () {
  522. try {
  523.   var dbService = Components.classes["@mozilla.org/storage/service;1"].getService(Components.interfaces.mozIStorageService);
  524.   var dirService = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
  525.   var dbFile = dirService.get("ProfDS", Components.interfaces.nsIFile);
  526.   dbFile.append('yoono');
  527.   dbFile.append('cookies.sqlite');
  528.   if (!dbFile.exists())
  529.     dbFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0655);
  530.   var dbConnection = dbService.openUnsharedDatabase(dbFile);
  531.   this._db = dbConnection;
  532.   //Components.utils.reportError("Current cookies DB version : "+dbConnection.schemaVersion);
  533.   if (dbConnection.schemaVersion==1) {
  534.     try {
  535.       dbConnection.executeSimpleSQL("ALTER TABLE sessions ADD COLUMN network TEXT");
  536.       dbConnection.executeSimpleSQL("ALTER TABLE sessions ADD COLUMN network_id INTEGER");
  537.     } catch(e) {
  538.       if (this._db)
  539.         Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  540.       else
  541.         Components.utils.reportError(e);
  542.     }
  543.     dbConnection.schemaVersion = 2;
  544.   }
  545.   if (dbConnection.schemaVersion==2) {
  546.     try {
  547.       dbConnection.executeSimpleSQL("ALTER TABLE sessions ADD COLUMN home TEXT");
  548.     } catch(e) {
  549.       if (this._db)
  550.         Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  551.       else
  552.         Components.utils.reportError(e);
  553.     }
  554.     dbConnection.schemaVersion = 3;
  555.   }
  556.   var currentVersion = 3;
  557.   if ( dbConnection.schemaVersion!=currentVersion ) {
  558.     try {
  559.       dbConnection.executeSimpleSQL("DROP TABLE cookies");
  560.     } catch(e) {}
  561.     
  562.     try {
  563.       dbConnection.executeSimpleSQL("DROP TABLE sessions");
  564.     } catch(e) {}
  565.     
  566.     var createCookiesTable = "CREATE TABLE cookies (";
  567.     createCookiesTable += "session_id TEXT, name TEXT, value TEXT, ";
  568.     createCookiesTable += "host TEXT, path TEXT, expiry INTEGER, ";
  569.     createCookiesTable += "lastAccessed INTEGER, isSecure INTEGER, ";
  570.     createCookiesTable += "isHttpOnly INTEGER, isSession INTEGER, ";
  571.     createCookiesTable += "PRIMARY KEY (session_id, name, host, path) ";
  572.     createCookiesTable += ")";
  573.     dbConnection.executeSimpleSQL(createCookiesTable);
  574.     
  575.     var createSessionsTable = "CREATE TABLE sessions (";
  576.     createSessionsTable += "id TEXT PRIMARY KEY, name TEXT, avatar BLOB, network TEXT, network_id INTEGER, home TEXT";
  577.     createSessionsTable += ")";
  578.     dbConnection.executeSimpleSQL(createSessionsTable);
  579.     
  580.     dbConnection.schemaVersion = currentVersion;
  581.     
  582.     return; // Don't do cleanup!
  583.   }
  584.   
  585.   // Performances improvements
  586.   dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF");
  587.   // remove this when debugging in order to use sqlitemanager when firefox is launched
  588.   dbConnection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
  589.   
  590.   var cleanup1 = this._db.createStatement("DELETE FROM cookies WHERE session_id like 'temp_%'");
  591.   cleanup1.executeAsync();
  592.   
  593.   var cleanup2 = this._db.createStatement("DELETE FROM sessions WHERE id like 'temp_%'");
  594.   cleanup2.executeAsync();
  595.   
  596.   var cleanup3 = this._db.createStatement("DELETE FROM cookies WHERE expiry >0 and expiry<:expiry");
  597.   cleanup3.params.expiry = new Date().getTime();
  598.   cleanup3.executeAsync();
  599.   
  600. } catch(e) {
  601.   if (this._db)
  602.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  603.   else
  604.     Components.utils.reportError(e);
  605. }
  606. }
  607.  
  608. CookiesStorage.deleteSession = function(id) {
  609. try {
  610.   if (!this._db) this._openDB();
  611.   this._db.beginTransaction();
  612.   var statement = this._db.createStatement("DELETE FROM cookies WHERE session_id = :id");
  613.   statement.params.id = id;
  614.   statement.executeStep();
  615.   var statement = this._db.createStatement("DELETE FROM sessions WHERE id = :id");
  616.   statement.params.id = id;
  617.   statement.executeStep();
  618.   this._db.commitTransaction();
  619. } catch(e) {
  620.   try {
  621.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError);
  622.   } catch(e) {
  623.     Components.utils.reportError(e);
  624.   }
  625. }
  626. }
  627.  
  628. CookiesStorage.renameSession = function(src, dst) {
  629. try {
  630.   if (!this._db) this._openDB();
  631.   this._db.beginTransaction();
  632.   var statement = this._db.createStatement("DELETE FROM cookies WHERE session_id = :dst");
  633.   statement.params.dst = dst;
  634.   statement.executeStep();
  635.   var statement = this._db.createStatement("DELETE FROM sessions WHERE id = :dst");
  636.   statement.params.dst = dst;
  637.   statement.executeStep();
  638.   var statement = this._db.createStatement("UPDATE sessions SET id=:dst WHERE id = :src");
  639.   statement.params.src = src;
  640.   statement.params.dst = dst;
  641.   statement.executeStep();
  642.   var statement = this._db.createStatement("UPDATE cookies SET session_id=:dst WHERE session_id = :src");
  643.   statement.params.src = src;
  644.   statement.params.dst = dst;
  645.   statement.executeStep();
  646.   this._db.commitTransaction();
  647.   
  648.   // TODO: do this anywhere else!
  649.   var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  650.                .getService(Components.interfaces.nsIWindowWatcher);
  651.   var enumerator = ww.getWindowEnumerator();
  652.   while (enumerator.hasMoreElements()) {
  653.     var win = enumerator.getNext();
  654.     if (!win.gBrowser) continue;
  655.     var num = win.gBrowser.mPanelContainer.childNodes.length;
  656.     for (var i = 0; i < num; i++) {
  657.       var browser = win.gBrowser.getBrowserAtIndex(i);
  658.       var tab = win.gBrowser.tabContainer.childNodes[i];
  659.       if (browser.__cookies && browser.__cookies._sessionId==src) {
  660.         browser.__cookies._sessionId = dst;
  661.         var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]
  662.                     .getService(Components.interfaces.nsISessionStore);
  663.         ss.setTabValue(tab, "cookies-db-id", dst);
  664.       }
  665.     }
  666.   }
  667.   if (this._updateBuffer[src])
  668.     this._updateBuffer[dst] = this._updateBuffer[src];
  669.   delete this._updateBuffer[src];
  670. } catch(e) {
  671.   try {
  672.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError);
  673.   } catch(e) {
  674.     Components.utils.reportError(e);
  675.   }
  676. }
  677. }
  678.  
  679. CookiesStorage.setSessionInfo = function (sessionId, name, avatar, network, networkId, homepageURL) {
  680. try {
  681.   if (!this._db) this._openDB();
  682.   var statement = this._db.createStatement("INSERT OR REPLACE INTO sessions (id, name, avatar, network, network_id, home) VALUES (:id, :name, :avatar, :network, :network_id, :home)");
  683.   statement.params.id = sessionId;
  684.   statement.params.name = name || "";
  685.   statement.params.avatar = avatar || "";
  686.   statement.params.network = network || "";
  687.   statement.params.network_id = networkId || -1;
  688.   statement.params.home = homepageURL || "";
  689.   statement.executeStep();
  690. } catch(e) {
  691.   if (this._db)
  692.     Components.utils.reportError(sessionId+"-"+name+"-"+avatar+"-"+network+"-"+networkId+" "+this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  693.   else
  694.     Components.utils.reportError(e);
  695. }
  696. }
  697.  
  698. CookiesStorage.getSessionInfo = function (sessionId, callback) {
  699. try {
  700.   var result;
  701.   if (!this._db) this._openDB();
  702.   var statement = this._db.createStatement("SELECT * FROM sessions WHERE id = :id");
  703.   statement.params.id = sessionId;
  704.   statement.executeAsync({  
  705.     handleResult: function(aResultSet) {
  706.       var row = aResultSet.getNextRow();
  707.       result = CookiesStorage._sqlToJSSession(row);
  708.     },
  709.  
  710.     handleError: function(aError) {
  711.       callback(result);
  712.     },
  713.  
  714.     handleCompletion: function(aReason) {
  715.       if (aReason != Components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
  716.         //print("Query canceled or aborted!");
  717.       }
  718.       callback(result);
  719.     }
  720.   });
  721. } catch(e) {
  722.   try {
  723.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError);
  724.   } catch(e) {
  725.     Components.utils.reportError(e);
  726.   }
  727. }
  728. }
  729.  
  730. CookiesStorage._sqlToJSSession = function (row) {
  731.   return {
  732.     id: row.getResultByName("id"),
  733.     name: row.getResultByName("name"),
  734.     avatar: row.getResultByName("avatar"),
  735.     network: row.getResultByName("network"),
  736.     networkId: row.getResultByName("network_id"),
  737.     homepage: row.getResultByName("home")
  738.   };
  739. }
  740.  
  741. CookiesStorage.getAllSessions = function (callback) {
  742. try {
  743.   var results = [];
  744.   if (!this._db) this._openDB();
  745.   var statement = this._db.createStatement("SELECT * FROM sessions");
  746.   statement.executeAsync({  
  747.     handleResult: function(aResultSet) {
  748.       while (row = aResultSet.getNextRow()) {
  749.         results.push( CookiesStorage._sqlToJSSession(row) );
  750.       }
  751.     },
  752.  
  753.     handleError: function(aError) {
  754.       callback(results);
  755.     },
  756.  
  757.     handleCompletion: function(aReason) {
  758.       if (aReason != Components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
  759.         //print("Query canceled or aborted!");
  760.       }
  761.       callback(results);
  762.     }
  763.   });
  764. } catch(e) {
  765.   try {
  766.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError);
  767.   } catch(e) {
  768.     Components.utils.reportError(e);
  769.   }
  770. }
  771. }
  772.  
  773. CookiesStorage.getCookiesForSession = function (sessionId, callback) {
  774. try {
  775.   var list = [];
  776.   if (!this._db) this._openDB();
  777.   
  778.   var statement = this._db.createStatement("SELECT * FROM cookies WHERE session_id = :session_id and (expiry=0 or expiry>=:expiry)");
  779.   statement.params.session_id = sessionId;
  780.   statement.params.expiry = new Date().getTime();
  781.   statement.executeAsync({  
  782.     handleResult: function(aResultSet) {
  783.       var row;
  784.       while (row = aResultSet.getNextRow()) {
  785.         var cookie = new Cookie({
  786.           name: row.getResultByName("name"),
  787.           value: row.getResultByName("value"), 
  788.           host: row.getResultByName("host"),
  789.           path: row.getResultByName("path"),
  790.           isSecure: row.getResultByName("isSecure")==1?true:false,
  791.           isHttpOnly: row.getResultByName("isHttpOnly")==1?true:false,
  792.           expires: null,
  793.           maxage: null
  794.         });
  795.         cookie.expiry = row.getResultByName("expiry");
  796.         if (cookie.expiry==0)
  797.           cookie.expiry = null;
  798.         cookie.isSession = row.getResultByName("isSession")==1?true:false;
  799.         list.push(cookie);
  800.       }
  801.       
  802.     },
  803.  
  804.     handleError: function(aError) {
  805.       //print("Error: " + aError.message);
  806.     },
  807.  
  808.     handleCompletion: function(aReason) {  
  809.       if (aReason != Components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
  810.         //print("Query canceled or aborted!");
  811.       }
  812.       callback(list);
  813.     }
  814.   });
  815. } catch(e) {
  816.   try {
  817.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError);
  818.   } catch(e) {
  819.     Components.utils.reportError(e);
  820.   }
  821. }
  822. }
  823.  
  824. CookiesStorage.deleteCookie = function (sessionId, cookie) {
  825. try {
  826.   if (this._updateBuffer && this._updateBuffer[sessionId]
  827.       && this._updateBuffer[sessionId][cookie.host]
  828.       && this._updateBuffer[sessionId][cookie.host][cookie.path])
  829.     delete this._updateBuffer[sessionId][cookie.host][cookie.path][cookie.name];
  830.   return;
  831.   if (cookie.name && cookie.name.length>0
  832.       && this.cookiesByDomain
  833.       && this.cookiesByDomain[cookie.host]
  834.       && this.cookiesByDomain[cookie.host][cookie.path]
  835.       && this.cookiesByDomain[cookie.host][cookie.path].named
  836.       && this.cookiesByDomain[cookie.host][cookie.path].named[cookie.name])
  837.     delete this.cookiesByDomain[cookie.host][cookie.path].named[cookie.name];
  838.   else if (this.cookiesByDomain && this.cookiesByDomain[cookie.host]
  839.           && this.cookiesByDomain[cookie.host][cookie.path]
  840.           && this.cookiesByDomain[cookie.host][cookie.path].unnamed
  841.           && this.cookiesByDomain[cookie.host][cookie.path].unnamed[cookie.value]) 
  842.     delete this.cookiesByDomain[cookie.host][cookie.path].unnamed[cookie.value];
  843.   return;
  844.   if (!this._db) this._openDB();
  845.   Components.utils.reportError("DELETE COOKIE : "+cookie.host+":"+cookie.path+" "+cookie.name);//+"/"+cookie.value);
  846.   var statement;
  847.   /*if (cookie.value)
  848.     statement = this._db.createStatement("DELETE FROM cookies WHERE session_id=:session_id and name=:name and value=:value and host=:host and path=:path");
  849.   else*/
  850.     statement = this._db.createStatement("DELETE FROM cookies WHERE session_id=:session_id and name=:name and host=:host and path=:path");
  851.   statement.params.session_id = sessionId;
  852.   statement.params.name = cookie.name;
  853.   //if (cookie.value)
  854.   //  statement.params.value = cookie.value;
  855.   statement.params.host = cookie.host;
  856.   statement.params.path = cookie.path;
  857.   statement.executeStep();
  858. } catch(e) {
  859.   try {
  860.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  861.   } catch(e) {
  862.     Components.utils.reportError("exception : "+e);
  863.   }
  864. }
  865. }
  866.  
  867. CookiesStorage._updateBuffer = {};
  868. CookiesStorage.updateCookie = function (sessionId, cookie) {
  869.   if (!this._updateBuffer[sessionId])
  870.     this._updateBuffer[sessionId] = {};
  871.   if (!this._updateBuffer[sessionId][cookie.host])
  872.     this._updateBuffer[sessionId][cookie.host] = {};
  873.   if (!this._updateBuffer[sessionId][cookie.host][cookie.path])
  874.     this._updateBuffer[sessionId][cookie.host][cookie.path] = {};
  875.   this._updateBuffer[sessionId][cookie.host][cookie.path][cookie.name] = cookie;
  876.   
  877.   if (this._bufferTimeout) return;
  878.   this._bufferTimeout =  Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  879.   this._bufferTimeout.initWithCallback({ notify: function () {
  880.     try {
  881.       CookiesStorage._bufferTimeout = null;
  882.       CookiesStorage._goUpdateBuffer();
  883.       log("CookiesStorage","Buffer saved!");
  884.     } catch(e) {
  885.       Components.utils.reportError(e);
  886.     }
  887.   }}, 4000,
  888.   Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  889. }
  890.  
  891. CookiesStorage._goUpdateBuffer = function () {
  892.   if (!this._db) this._openDB();
  893.   
  894.   if (!this.statementWithValue || !this.statementWithoutValue) {
  895.     var insertSQL1 = "";
  896.     insertSQL1 += "INSERT OR REPLACE INTO cookies ";
  897.     insertSQL1 += "(session_id, name, ";
  898.     insertSQL1 += "host, path, expiry, lastAccessed, isSecure, isHttpOnly, isSession) ";
  899.     insertSQL1 += "VALUES ";
  900.     insertSQL1 += "(:session_id, :name, ";
  901.     insertSQL1 += ":host, :path, :expiry, DATETIME('NOW'), :is_secure, :is_http_only, :is_session)";
  902.     
  903.     var insertSQL2 = "";
  904.     insertSQL2 += "INSERT OR REPLACE INTO cookies ";
  905.     insertSQL2 += "(session_id, name, ";
  906.     insertSQL2 += "value, ";
  907.     insertSQL2 += "host, path, expiry, lastAccessed, isSecure, isHttpOnly, isSession) ";
  908.     insertSQL2 += "VALUES ";
  909.     insertSQL2 += "(:session_id, :name, ";
  910.     insertSQL2 += ":value, ";
  911.     insertSQL2 += ":host, :path, :expiry, DATETIME('NOW'), :is_secure, :is_http_only, :is_session)";
  912.     
  913.     this.statementWithoutValue = this._db.createStatement(insertSQL1);
  914.     this.statementWithValue = this._db.createStatement(insertSQL2);
  915.   }
  916.   var start=new Date().getTime();
  917.   var count = 0;
  918.   this._db.beginTransaction();
  919.   for(var session in this._updateBuffer) {
  920.     var bySession = this._updateBuffer[session];
  921.     for(var host in bySession) {
  922.       var byHost = bySession[host];
  923.       for(var path in byHost) {
  924.         var byPath = byHost[path];
  925.         for(var name in byPath) {
  926.           //log("CookiesStorage","save this : "+name);
  927.           this._goUpdateOne(session, byPath[name]);
  928.           count++;
  929.         }
  930.       }
  931.     }
  932.   }
  933.   this._db.commitTransaction();
  934.   //Components.utils.reportError(count+" cookies updates in "+(new Date().getTime()-start)+"ms");
  935.   this._updateBuffer = {};
  936. }
  937.  
  938. CookiesStorage._goUpdateOne = function (sessionId, cookie) {
  939. try {
  940.   var statement = cookie.value?this.statementWithValue:this.statementWithoutValue;
  941.   statement.params.session_id = sessionId;
  942.   statement.params.name = cookie.name;
  943.   if (cookie.value)
  944.     statement.params.value = cookie.value;
  945.   statement.params.host = cookie.host;
  946.   statement.params.path = cookie.path;
  947.   statement.params.expiry = cookie.expiry?cookie.expiry:0;
  948.   statement.params.is_secure = cookie.isSecure?1:0;
  949.   statement.params.is_http_only = cookie.isHttpOnly?1:0;
  950.   statement.params.is_session = cookie.isSession?1:0;
  951.   
  952.   statement.executeAsync();
  953. } catch(e) {
  954.   try {
  955.     Components.utils.reportError(this._db.lastErrorString+" / "+this._db.lastError+"\n"+e);
  956.   } catch(e) {
  957.     Components.utils.reportError(e);
  958.   }
  959. }
  960. }
  961.  
  962.  
  963.  
  964. ///////////////////////////////////////////////////////////////////////////
  965. // Class CookiesDB
  966. //-------------------------------------------------------------------------
  967. // "Man is the middle"
  968. // Mean to be an instance of one independant Cookies database
  969. // Main object between website (http request/JS cookie attribute) and Cookies class/services
  970. //
  971. function CookiesDB(sessionId, onReadyCallback) {
  972.   this._sessionId = sessionId;
  973.   this.cookiesByDomain = {};
  974.   var _self = this;
  975.   CookiesStorage.getCookiesForSession(sessionId, 
  976.     function (cookies) {
  977.       for(var i=0; i<cookies.length; i++) {
  978.         _self._addCookie(cookies[i]);
  979.       }
  980.       if (typeof onReadyCallback=="function")
  981.         onReadyCallback();
  982.     });
  983. }
  984.  
  985. CookiesDB.prototype._addCookie = function (cookie) {
  986.   if (!this.cookiesByDomain[cookie.host])
  987.     this.cookiesByDomain[cookie.host] = {};
  988.   if (!this.cookiesByDomain[cookie.host][cookie.path])
  989.     this.cookiesByDomain[cookie.host][cookie.path] = {named:{},unnamed:{}};
  990.   
  991.   if (cookie.name && cookie.name.length>0)
  992.     delete this.cookiesByDomain[cookie.host][cookie.path].named[cookie.name];
  993.   else
  994.     delete this.cookiesByDomain[cookie.host][cookie.path].unnamed[cookie.value];
  995.   
  996.   if (cookie.expiry && cookie.expiry <= new Date().getTime()) {
  997.     //if (!cookie.isSession)
  998.     //  CookiesStorage.deleteCookie(this._sessionId, cookie);
  999.     //return log("SetCondition","cookie expired!");
  1000.   } else {
  1001.     //log("SetCondition",cookie.name+" not expired : "+cookie.expiry+" <= "+new Date().getTime());
  1002.   }
  1003.   
  1004.   if (cookie.name && cookie.name.length>0)
  1005.     this.cookiesByDomain[cookie.host][cookie.path].named[cookie.name]=cookie;
  1006.   else
  1007.     this.cookiesByDomain[cookie.host][cookie.path].unnamed[cookie.value]=cookie;
  1008.   
  1009.   //if (!cookie.isSession) {
  1010.     CookiesStorage.updateCookie(this._sessionId, cookie);
  1011.   //}
  1012. }
  1013.  
  1014. CookiesDB.prototype._getCookiesForDomain = function (domain) {
  1015.   var byDomain = this.cookiesByDomain[domain];
  1016.   //log("GetCondition","getCookiesForDomain("+domain+")="+(byDomain?byDomain.length:"null"));
  1017.   if (!byDomain) return [];
  1018.   var list=[];
  1019.   for(var path in byDomain) {
  1020.     var byPath = byDomain[path];
  1021.     for(var name in byPath.named) {
  1022.       list.push(byPath.named[name])
  1023.     }
  1024.     for(var v in byPath.unnamed) {
  1025.       list.push(byPath.unnamed[v])
  1026.     }
  1027.   }
  1028.   return list;
  1029. }
  1030.  
  1031. CookiesDB.prototype._getCookieString = function (hostFromURI, pathFromURI, isSecure, aHttpBound) {
  1032.   // JS equivalent of GetCookieInternal (part of)
  1033.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/src/nsCookieService.cpp#1218
  1034.   
  1035.   // trim trailing dots
  1036.   hostFromURI = hostFromURI.replace(/(^\.)|(\.$)/g,"")
  1037.   // insert a leading dot, so we begin the hash lookup with the
  1038.   // equivalent domain cookie host
  1039.   hostFromURI = "."+hostFromURI;
  1040.   
  1041.   var currentDot = hostFromURI;
  1042.   var nextDot = hostFromURI.substring(1);
  1043.   
  1044.   var foundCookieList = [];
  1045.   
  1046.   // begin hash lookup, walking up the subdomain levels.
  1047.   // we use nextDot to force a lookup of the original host (without leading dot).
  1048.   do {
  1049.     var list = this._getCookiesForDomain(currentDot);
  1050.     for (var i=0; i<list.length; i++) {
  1051.       var cookie = list[i];
  1052.       
  1053.       if (!cookie.canGetFrom(pathFromURI, isSecure, aHttpBound)) continue;
  1054.       
  1055.       // all checks passed - add to list and check if lastAccessed stamp needs updating
  1056.       foundCookieList.push(cookie);
  1057.     }
  1058.  
  1059.     currentDot = nextDot;
  1060.     if (currentDot) {
  1061.       var temp = currentDot.substring(1);
  1062.       var idx = temp.indexOf('.');
  1063.       if (idx==-1)
  1064.         nextDot = null;
  1065.       else
  1066.         nextDot = temp.substring(idx);
  1067.     }
  1068.  
  1069.   } while (currentDot && currentDot.length>0);
  1070.  
  1071.   if (foundCookieList.length == 0)
  1072.     return "";
  1073.   
  1074.   // return cookies in order of path length; longest to shortest.
  1075.   // this is required per RFC2109.  if cookies match in length,
  1076.   // then sort by creation time (see bug 236772).
  1077.   // foundCookieList.Sort(CompareCookiesForSendingComparator());
  1078.   
  1079.   return Cookie.cookiesListToString(foundCookieList);
  1080. }
  1081.  
  1082. CookiesDB.prototype.getCookieStringFromJS = function (location) {
  1083.   var path = location.pathname;
  1084.   var idx = path.lastIndexOf('/');
  1085.   if (idx!=-1)
  1086.     path = path.substring(0,idx);
  1087.   return this._getCookieString(location.host, path, location.protocol=="https:", false);
  1088. }
  1089.  
  1090. const ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);  
  1091.  
  1092. CookiesDB.prototype.setCookieStringFromJS = function (location, str) {
  1093.   var uri = ioService.newURI(location.href, null, null);
  1094.   var iterator = new CookieStringIterator(str);
  1095.   var cookie = iterator.getNext();
  1096.   if (cookie && cookie.canSetFrom(uri)) {
  1097.     cookie.computeExpiracy(new Date().getTime());
  1098.     this._addCookie(cookie);
  1099.   }
  1100. }
  1101.  
  1102. CookiesDB.prototype.getCookieStringFromHttp = function (uri) {
  1103.   // get host and path from the nsIURI
  1104.   // note: there was a "check if host has embedded whitespace" here.
  1105.   // it was removed since this check was added into the nsIURI impl (bug 146094).
  1106.   var hostFromURI = uri.asciiHost;
  1107.   var pathFromURI = uri.path;
  1108.   
  1109.   // check if aHostURI is using an https secure protocol.
  1110.   // if it isn't, then we can't send a secure cookie over the connection.
  1111.   // if SchemeIs fails, assume an insecure connection, to be on the safe side
  1112.   var isSecure = uri.schemeIs("https");
  1113.   
  1114.   return this._getCookieString(hostFromURI, pathFromURI, isSecure, true);
  1115. }
  1116.  
  1117. CookiesDB.prototype.setCookieStringFromHttp = function (dateHeader, cookiesHeader, uri) {
  1118.   var serverTime = new Date().getTime();
  1119.   try {
  1120.     serverTime = Cookie.parseCookieDate(dateHeader).getTime();
  1121.   } catch(e) {}
  1122.   
  1123.   var iterator = new CookieStringIterator(cookiesHeader);
  1124.   while(iterator.hasNext()) {
  1125.     var cookie = iterator.getNext();
  1126.     if (cookie && cookie.canSetFrom(uri)) {
  1127.       cookie.computeExpiracy(serverTime);
  1128.       this._addCookie(cookie);
  1129.     }
  1130.   }
  1131. }
  1132.  
  1133.  
  1134.  
  1135.  
  1136. ///////////////////////////////////////////////////////////////////////////
  1137. // Service MainRequestsObserver 
  1138. //-------------------------------------------------------------------------
  1139. // Watch for all outgoing and ingoing HTTP requests
  1140. // in order to replace cookies going out with cookies from our database
  1141. // and to put cookies going in into our database
  1142. //
  1143. const MainRequestsObserver = {};
  1144. const nsILoadContext = Components.interfaces.nsILoadContext;
  1145. MainRequestsObserver.findBrowserForRequest = function(channel) {
  1146.   
  1147.   // First, we need to find loadContext for this request
  1148.   // channel is an nsIHttpChannel which inherits from nsIChannel then from nsIRequest :
  1149.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIChannel.idl#60
  1150.   // \--> notificationCallbacks
  1151.   // http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIRequest.idl#50
  1152.   // \--> loadGroup
  1153.   
  1154.   var loadContext;
  1155.   try {
  1156.     loadContext = channel.notificationCallbacks.getInterface(nsILoadContext);
  1157.   } catch (ex) {
  1158.     try {
  1159.       loadContext = channel.loadGroup.notificationCallbacks.getInterface(nsILoadContext);
  1160.     } catch (ex) {
  1161.       loadContext = null;
  1162.     }
  1163.   }
  1164.   
  1165.   // Some firefox internal request doesn't have loadGroup : 
  1166.   // - safebrowsing requests
  1167.   // - search suggest queries
  1168.   // - ssl certificate verification
  1169.   // ...
  1170.   
  1171.   // But for web content, it appears that only this case doesn't have loadGroup :
  1172.   // - CSS Icons explicitely lets loadGroup empty : 
  1173.   // http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsImageFrame.cpp#1653
  1174.   
  1175.   if (!loadContext) {
  1176.     //log("FindBrowser","Unable to find load context for "+channel.URI.spec+"\n");
  1177.     return null;
  1178.   }
  1179.   
  1180.   // Then we just have to retrieve associated window from the loadContext
  1181.   // http://mxr.mozilla.org/mozilla-central/source/docshell/base/nsILoadContext.idl
  1182.   
  1183.   var win = loadContext.associatedWindow;
  1184.   if (!win) {
  1185.     //log("FindBrowser","Unable to find associated window for "+channel.URI.spec+"\n");
  1186.     return null;
  1187.   }
  1188.   
  1189.   // Now get the top xul ChromeWindow object with some QueryInterface magic
  1190.   // (all chrome requests fail here)
  1191.   var browserWindow;
  1192.   try {
  1193.     browserWindow = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  1194.                    .getInterface(Components.interfaces.nsIWebNavigation)
  1195.                    .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
  1196.                    .rootTreeItem
  1197.                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  1198.                    .getInterface(Components.interfaces.nsIDOMWindow).wrappedJSObject; 
  1199.   } catch(e) {}
  1200.   
  1201.   if (!browserWindow) {
  1202.     //log("FindBrowser","Unable to find top xul window "+channel.URI.spec+"\n");
  1203.     return null;
  1204.   }
  1205.   
  1206.   // Need to iterate above windows to go through iframes up to the top content window
  1207.   while( win!=win.top ) {
  1208.     win = win.top;
  1209.   }
  1210.   
  1211.   // And finally, find the matching tab
  1212.   var browser;
  1213.   if (browserWindow.gBrowser)
  1214.     browser = browserWindow.gBrowser.getBrowserForDocument(win.document);
  1215.   if (browserWindow.opener && browserWindow.opener.ynWebPanel)
  1216.     browser = browserWindow.opener.ynWebPanel.getBrowser();
  1217.   
  1218.   if (!browser) {
  1219.     //log("FindBrowser","Unable to find browser "+channel.URI.spec+"\n");
  1220.     return null;
  1221.   }
  1222.   
  1223.   //log("FindBrowser","Found browser for "+channel.URI.spec+"\n");
  1224.   return browser;
  1225. }
  1226.  
  1227. MainRequestsObserver.observe = function (channel, aTopic, aData) {
  1228. try {
  1229.   if (aTopic == 'http-on-modify-request') {
  1230.     
  1231.     channel.QueryInterface(Components.interfaces.nsIHttpChannel);
  1232.     
  1233.     var browser = MainRequestsObserver.findBrowserForRequest(channel);
  1234.     if (!browser) return;
  1235.       
  1236.     if (!browser.__cookies && !browser.__cookiesChecked) {
  1237.       
  1238.       browser.__cookiesChecked = true; // try to restore cookies DB only once
  1239.       
  1240.       // First try to retrieve a cookie session id from session store 
  1241.       if (CookiesService._listener) {
  1242.         var session_id = CookiesService._listener.getBrowserSessionId(browser);
  1243.         if (session_id) {
  1244.           CookiesService.attachSessionToBrowser(browser, session_id);
  1245.         }
  1246.       }
  1247.       
  1248.       // Then try to find if the current document is opened by another one
  1249.       // typically: javascript window.open calls!
  1250.       var openerWindow = browser.contentWindow.opener;
  1251.       var openerBrowser = CookiesProgressListener._findBrowserForContentWindow(openerWindow);
  1252.       if (openerBrowser && openerBrowser.__cookies) {
  1253.         Components.utils.reportError("sessionId from opener : "+openerBrowser.__cookies._sessionId);
  1254.         browser.__cookies = openerBrowser.__cookies;
  1255.         CookiesService._listener.onBrowserAttached(browser, browser.__cookies._sessionId);
  1256.       }
  1257.       
  1258.     }
  1259.     
  1260.     if (!browser.__cookies) {
  1261.       return;
  1262.     }
  1263.     
  1264.     // Get cookies from our DB and replace HTTP Cookie header
  1265.     var cookiesHeader = browser.__cookies.getCookieStringFromHttp(channel.URI);
  1266.     channel.setRequestHeader("Cookie",cookiesHeader,false);
  1267.     
  1268.     log("HTTP","SEND cookies : "+channel.URI.spec+"\n<<"+cookiesHeader+">>");
  1269.     
  1270.   } else if (aTopic == 'http-on-examine-response') {
  1271.     
  1272.     channel.QueryInterface(Components.interfaces.nsIHttpChannel);
  1273.     
  1274.     var browser = MainRequestsObserver.findBrowserForRequest(channel);
  1275.     if (!browser) return;
  1276.     
  1277.     if (!browser.__cookies) {
  1278.       return;
  1279.     }
  1280.     
  1281.     // Get Set-Cookie header and update our cookie database
  1282.     var cookiesHeader;
  1283.     try {
  1284.       cookiesHeader = channel.getResponseHeader("Set-Cookie");
  1285.     } catch(e) { cookiesHeader = null; }
  1286.     if (!cookiesHeader) return;
  1287.     
  1288.     log("HTTP","RECEIVE cookies : "+channel.URI.spec+"\n<<"+cookiesHeader+">>");
  1289.     
  1290.     var dateHeader;
  1291.     try {
  1292.       dateHeader = channel.getResponseHeader("Date");
  1293.     } catch(e){}
  1294.     
  1295.     browser.__cookies.setCookieStringFromHttp(dateHeader, cookiesHeader, channel.URI);
  1296.     
  1297.     // Reset cookie header in order to avoid mozilla to take account of these cookies
  1298.     channel.setResponseHeader("Set-Cookie", "", false);
  1299.     
  1300.   }
  1301. } catch(e) {Components.utils.reportError(e);}
  1302. }
  1303. MainRequestsObserver.start = function () {
  1304.   var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
  1305.   observerService.addObserver(this, "http-on-modify-request", false);
  1306.   observerService.addObserver(this, "http-on-examine-response", false);
  1307. }
  1308. MainRequestsObserver.stop = function () {
  1309.   var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
  1310.   observerService.removeObserver(this, "http-on-modify-request", false);
  1311.   observerService.removeObserver(this, "http-on-examine-response", false);
  1312. }
  1313.  
  1314.  
  1315.  
  1316. ///////////////////////////////////////////////////////////////////////////
  1317. // CookiesProgressListener 
  1318. //-------------------------------------------------------------------------
  1319. // Watch for new tab or location change 
  1320. // in order to bind our self document.cookie attribute 
  1321. // which will retrieve cookies from our database
  1322. //
  1323. const CookiesProgressListener = {};
  1324. const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
  1325. CookiesProgressListener._findBrowserForContentWindow = function (win) {
  1326.   // Now get the top xul ChromeWindow object with some QueryInterface magic
  1327.   // (all chrome requests fail here)
  1328.   var browserWindow;
  1329.   try {
  1330.     browserWindow = win.QueryInterface(nsIInterfaceRequestor)
  1331.                    .getInterface(Components.interfaces.nsIWebNavigation)
  1332.                    .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
  1333.                    .rootTreeItem
  1334.                    .QueryInterface(nsIInterfaceRequestor)
  1335.                    .getInterface(Components.interfaces.nsIDOMWindow).wrappedJSObject; 
  1336.   } catch(e) {}
  1337.   
  1338.   if (!browserWindow) {
  1339.     //log("FindBrowser","Unable to find top xul window "+channel.URI.spec+"\n");
  1340.     return null;
  1341.   }
  1342.   
  1343.   // Need to iterate above windows to go through iframes up to the top content window
  1344.   while( win!=win.top ) {
  1345.     win = win.top;
  1346.   }
  1347.   
  1348.   // And finally, find the matching tab
  1349.   var browser;
  1350.   if (browserWindow.gBrowser)
  1351.     browser = browserWindow.gBrowser.getBrowserForDocument(win.document);
  1352.   if (browserWindow.opener && browserWindow.opener.ynWebPanel)
  1353.     browser = browserWindow.opener.ynWebPanel.getBrowser();
  1354.   
  1355.   if (!browser) {
  1356.     //log("FindBrowser","Unable to find browser "+channel.URI.spec+"\n");
  1357.     return null;
  1358.   }
  1359.   
  1360.   return browser;
  1361. }
  1362.  
  1363. CookiesProgressListener.bindCookiesAttribute = function(win) {
  1364.   if (win.___cookiesHooked) return;
  1365.   win.___cookiesHooked = true;
  1366.   log("NewContentDocument","URL : "+win.document.location);
  1367.   
  1368.   var browser = CookiesProgressListener._findBrowserForContentWindow(win);
  1369.   if (!browser) return;
  1370.   if (!browser.__cookies) {
  1371.     
  1372.     return;
  1373.   }
  1374.   
  1375.   var doc = win.document;
  1376.   doc.wrappedJSObject.__defineSetter__("cookie", function(v) {
  1377.     try {
  1378.       if (!browser.__cookies) return;
  1379.       log("JSCookie","SET from "+doc.location+"\n"+v);
  1380.       browser.__cookies.setCookieStringFromJS(doc.location, v);
  1381.     } catch(e) {
  1382.       Components.utils.reportError(e);
  1383.     }
  1384.   });
  1385.   doc.wrappedJSObject.__defineGetter__("cookie", function() {
  1386.     try {
  1387.       if (!browser.__cookies) return;
  1388.       var s = browser.__cookies.getCookieStringFromJS(doc.location);
  1389.       log("JSCookie","GET from "+doc.location+"\n"+s);
  1390.       return s;
  1391.     } catch(e) {
  1392.       Components.utils.reportError(e);
  1393.     }
  1394.   });
  1395.   
  1396. }
  1397.   
  1398. CookiesProgressListener.onLocationChange = function(webProgress, request, location) {
  1399.   //Components.utils.reportError("location change : "+(request?request.name:"")+" --- "+(location?location.href:""));
  1400.   var win = webProgress.DOMWindow;
  1401.   if (!win) return;
  1402.   CookiesProgressListener.bindCookiesAttribute(win);
  1403. }
  1404.  
  1405. CookiesProgressListener.onProgressChange = function(webProgress, request, curSelfProgress) {
  1406.   //Components.utils.reportError("progress change : "+(request?request.name:"")+" --- "+curSelfProgress);
  1407. }
  1408. CookiesProgressListener.onSecurityChange = function(webProgress, request, state) {
  1409.   //Components.utils.reportError("security change : "+(request?request.name:"")+" --- "+state);
  1410. }
  1411.  
  1412. CookiesProgressListener.onStateChange = function(webProgress, request, stateFlags, status) {
  1413.   /*
  1414.   Components.utils.reportError("state change : "+(request?request.name:"")+" --- "+stateFlags);
  1415.   Components.utils.reportError("START="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_START));
  1416.   Components.utils.reportError("REDIRECTING="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_REDIRECTING));
  1417.   Components.utils.reportError("STATE_TRANSFERRING="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_TRANSFERRING));
  1418.   Components.utils.reportError("STATE_NEGOTIATING="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_NEGOTIATING));
  1419.   Components.utils.reportError("STATE_STOP="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_STOP));
  1420.   Components.utils.reportError("STATE_IS_REQUEST="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_IS_REQUEST));
  1421.   Components.utils.reportError("STATE_IS_DOCUMENT="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT));
  1422.   Components.utils.reportError("STATE_IS_NETWORK="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK));
  1423.   Components.utils.reportError("STATE_IS_WINDOW="+(stateFlags&Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW));
  1424.   */
  1425.   //if(stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) {
  1426.     var win = webProgress.DOMWindow;
  1427.     if (!win) return;
  1428.     CookiesProgressListener.bindCookiesAttribute(win);
  1429.   //}
  1430. }
  1431.  
  1432. CookiesProgressListener.onStatusChange = function(webProgress, request, status, message) {
  1433.   //Components.utils.reportError("status change : "+(request?request.name:"")+" --- "+status+" - "+message);
  1434. }
  1435.  
  1436. CookiesProgressListener.QueryInterface = function(aIID) { 
  1437.   if (aIID.equals(Components.interfaces.nsIWebProgressListener) || 
  1438.      aIID.equals(Components.interfaces.nsISupportsWeakReference) || 
  1439.      aIID.equals(Components.interfaces.nsIObserver) || 
  1440.      aIID.equals(Components.interfaces.nsISupports)) 
  1441.     return this; 
  1442.   throw Components.results.NS_NOINTERFACE; 
  1443. }
  1444.  
  1445. CookiesProgressListener.addProgressListener = function (prog) {
  1446.   prog.addProgressListener(CookiesProgressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1447. }
  1448.  
  1449. CookiesProgressListener.removeProgressListener = function (prog) {
  1450.   prog.removeProgressListener(CookiesProgressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1451. }
  1452.  
  1453.  
  1454.  
  1455. ///////////////////////////////////////////////////////////////////////////
  1456. // Service FirefoxWindowsWatcher 
  1457. //-------------------------------------------------------------------------
  1458. // Utils for Firefox,
  1459. // Automatically register all browsers to CookiesProgressListener
  1460. //
  1461. var FirefoxWindowsWatcher = {};
  1462. FirefoxWindowsWatcher.QueryInterface = function(aIID) { 
  1463.   if (aIID.equals(Components.interfaces.nsISupportsWeakReference) || 
  1464.      aIID.equals(Components.interfaces.nsIObserver) || 
  1465.      aIID.equals(Components.interfaces.nsISupports)) 
  1466.     return this; 
  1467.   throw Components.results.NS_NOINTERFACE; 
  1468. }
  1469.  
  1470. FirefoxWindowsWatcher._onWindowLoad = function (win) {
  1471.   CookiesProgressListener.addProgressListener(win.gBrowser);
  1472.   
  1473.   // Watch for website inner link clicks
  1474.   var Ci = Components.interfaces;
  1475.   var previous = win.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow;
  1476.   win.QueryInterface(Components.interfaces.nsIDOMChromeWindow).browserDOMWindow = {
  1477.     //QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
  1478.     openURI: function (aURI, aOpener, aWhere, aContext) {
  1479.       //Components.utils.reportError("openURI");
  1480.       var newWindow = previous.openURI(aURI, aOpener, aWhere, aContext);
  1481.       try {
  1482.       //Components.utils.reportError("newWindow : "+newWindow);
  1483.       var openerBrowser;
  1484.       if (aOpener)
  1485.         openerBrowser = CookiesProgressListener._findBrowserForContentWindow(aOpener);
  1486.       //Components.utils.reportError("openerBrowser : "+openerBrowser);
  1487.       if (openerBrowser && openerBrowser.__cookies) {
  1488.         var newBrowser = CookiesProgressListener._findBrowserForContentWindow(newWindow);
  1489.         //Components.utils.reportError("newBrowser : "+newBrowser);
  1490.         if (newBrowser) {
  1491.           Components.utils.reportError("sessionId : "+openerBrowser.__cookies._sessionId);
  1492.           newBrowser.__cookies = openerBrowser.__cookies;
  1493.           //var reloadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
  1494.           //newBrowser.reload(reloadFlags);
  1495.           CookiesService._listener.onBrowserAttached(newBrowser, newBrowser.__cookies._sessionId);
  1496.         }
  1497.       }
  1498.       }catch(e){
  1499.         Components.utils.reportError(e);
  1500.       }
  1501.       return newWindow;
  1502.     },
  1503.     isTabContentWindow: function (aWindow) {
  1504.       return previous.isTabContentWindow(aWindow);
  1505.     }
  1506.   };
  1507.   
  1508.   // Watch for context menu->open in tab
  1509.   var previousOpenNewTabWith = win.openNewTabWith;
  1510.   win.openNewTabWith = function (aURL, aDocument, aPostData, aEvent, aAllowThirdPartyFixup, aReferrer) {
  1511.     //Components.utils.reportError("openNewTabWith");
  1512.     var openerBrowser;
  1513.     var newTab;
  1514.     if (aDocument && aDocument.defaultView)
  1515.       openerBrowser = CookiesProgressListener._findBrowserForContentWindow(aDocument.defaultView);
  1516.     if (openerBrowser && openerBrowser.__cookies) {
  1517.       newTab = previousOpenNewTabWith.call(win, "about:blank", aDocument, null, null, null, null);
  1518.       var newBrowser;
  1519.       if (newTab && newTab.linkedBrowser)
  1520.         newBrowser = newTab.linkedBrowser;
  1521.       if (newBrowser) {
  1522.         //Components.utils.reportError("sessionId from new tab : "+openerBrowser.__cookies._sessionId);
  1523.         newBrowser.__cookies = openerBrowser.__cookies;
  1524.         CookiesService._listener.onBrowserAttached(newBrowser, newBrowser.__cookies._sessionId);
  1525.         var charsetArg = null;
  1526.         var wintype = newBrowser.ownerDocument.documentElement.getAttribute("windowtype");
  1527.         if (wintype == "navigator:browser")
  1528.           charsetArg = "charset=" + newBrowser.ownerDocument.defaultView.content.document.characterSet;
  1529.         
  1530.         var referrerURI = aDocument ? aDocument.documentURIObject : aReferrer;
  1531.         if (aPostData === undefined)
  1532.           aPostData = null;
  1533.         var flags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
  1534.         if (aAllowThirdPartyFixup) {
  1535.           flags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1536.         }
  1537.         newBrowser._originalLoadURIWithFlags(aURL, flags, referrerURI, charsetArg, aPostData);
  1538.       }
  1539.     } else {
  1540.       newTab = previousOpenNewTabWith.call(win, aURL, aDocument, aPostData, aEvent, aAllowThirdPartyFixup, aReferrer);
  1541.     }
  1542.     return newTab;
  1543.   };
  1544.   
  1545.   // Watch for context menu->open in window
  1546.   var previousOpenNewWindowWith = win.openNewWindowWith;
  1547.   win.openNewWindowWith = function (aURL, aDocument, aPostData, aAllowThirdPartyFixup, aReferrer) {
  1548.     //Components.utils.reportError("openNewWindowWith");
  1549.     var newWindow;
  1550.     var openerBrowser;
  1551.     if (aDocument && aDocument.defaultView)
  1552.       openerBrowser = CookiesProgressListener._findBrowserForContentWindow(aDocument.defaultView);
  1553.     if (openerBrowser && openerBrowser.__cookies) {
  1554.       newWindow = previousOpenNewWindowWith.call(win, "about:blank", aDocument, null, null, null);
  1555.       newWindow.addEventListener("load",function () {
  1556.         newWindow.removeEventListener("load",arguments.callee,false);
  1557.         var newBrowser;
  1558.         if (newWindow && newWindow.gBrowser && newWindow.gBrowser.mCurrentBrowser)
  1559.           newBrowser = newWindow.gBrowser.mCurrentBrowser;
  1560.         
  1561.         if (newBrowser) {
  1562.           //Components.utils.reportError("sessionId : "+openerBrowser.__cookies._sessionId);
  1563.           newBrowser.__cookies = openerBrowser.__cookies;
  1564.           CookiesService._listener.onBrowserAttached(newBrowser, newBrowser.__cookies._sessionId);
  1565.           var charsetArg = null;
  1566.           var wintype = newBrowser.ownerDocument.documentElement.getAttribute("windowtype");
  1567.           if (wintype == "navigator:browser")
  1568.             charsetArg = "charset=" + newBrowser.ownerDocument.defaultView.content.document.characterSet;
  1569.           
  1570.           var referrerURI = aDocument ? aDocument.documentURIObject : aReferrer;
  1571.           if (aPostData === undefined)
  1572.             aPostData = null;
  1573.           var flags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
  1574.           if (aAllowThirdPartyFixup) {
  1575.             flags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1576.           }
  1577.  
  1578.           newBrowser._originalLoadURIWithFlags(aURL, flags, referrerURI, charsetArg, aPostData);
  1579.         }
  1580.       }, false);
  1581.     } else {
  1582.       newWindow = previousOpenNewWindowWith.call(win, aURL, aDocument, aPostData, aAllowThirdPartyFixup, aReferrer);
  1583.     }
  1584.     return newWindow;
  1585.   };
  1586. }
  1587.  
  1588. FirefoxWindowsWatcher.observe = function (aSubject, aTopic, aData) {
  1589.   if (aTopic == "domwindowopened") {
  1590.     var win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
  1591.     if (!win.gBrowser) return;
  1592.     var type = win.document.documentElement.getAttribute("windowtype");
  1593.     if (!type) {
  1594.       win.addEventListener("load",function () {
  1595.         win.removeEventListener("load", arguments.callee, false);
  1596.         if (win.document.documentElement.getAttribute("windowtype") != "navigator:browser") return;
  1597.         FirefoxWindowsWatcher._onWindowLoad(win);
  1598.       }, false);
  1599.     } else if (type == "navigator:browser") {
  1600.       FirefoxWindowsWatcher._onWindowLoad(win);
  1601.     }
  1602.   }
  1603.   else if (aTopic == "domwindowclosed") {
  1604.     var win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
  1605.     if (win.document.documentElement.getAttribute("windowtype") != "navigator:browser") return;
  1606.     if (!win.gBrowser) return;
  1607.     CookiesProgressListener.removeProgressListener(win.gBrowser);
  1608.   }
  1609. }
  1610.  
  1611. FirefoxWindowsWatcher.start = function () {
  1612.   var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1613.                .getService(Components.interfaces.nsIWindowWatcher);
  1614.   var enumerator = ww.getWindowEnumerator();
  1615.   while (enumerator.hasMoreElements()) {
  1616.     FirefoxWindowsWatcher.observe(enumerator.getNext(),"domwindowopened",null);
  1617.   }
  1618.   ww.registerNotification(this);
  1619. }
  1620.  
  1621. FirefoxWindowsWatcher.stop = function () {
  1622.   var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1623.                .getService(Components.interfaces.nsIWindowWatcher);
  1624.   ww.unregisterNotification(this);
  1625.   var enumerator = ww.getWindowEnumerator();
  1626.   while (enumerator.hasMoreElements()) {
  1627.     FirefoxWindowsWatcher.observe(enumerator.getNext(),"domwindowclosed",null);
  1628.   }
  1629. }
  1630.  
  1631.  
  1632.  
  1633.  
  1634. ////////////////////////////////////////////////////////////////////////////////
  1635. // JS Module API
  1636. // 
  1637. const CookiesService = {
  1638.   _listener : null,
  1639.   
  1640.   FirefoxWindowsWatcher : FirefoxWindowsWatcher,
  1641.   
  1642.   init : function (listener) {
  1643.     try {
  1644.       if (typeof listener!="object") throw "CookiesService.init: listener is mandatory";
  1645.       if (typeof listener.getBrowserSessionId!="function") throw "CookiesService.init: listener need to have a getBrowserSessionId function";
  1646.       if (typeof listener.onBrowserAttached!="function") throw "CookiesService.init: listener need to have a onBrowserAttached function";
  1647.       this._listener = listener;
  1648.       
  1649.       MainRequestsObserver.start();
  1650.     } catch(e) {
  1651.       Components.utils.reportError(e);
  1652.     }
  1653.   },
  1654.   
  1655.   // Register a new <browser> object
  1656.   // (Allow to catch JS getter/setter of cookies)
  1657.   registerBrowser : function (browser) {
  1658.     var req = browser.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  1659.     var prog = req.getInterface(Components.interfaces.nsIWebProgress);
  1660.     CookiesProgressListener.addProgressListener(prog);
  1661.   },
  1662.   unregisterBrowser : function (browser) {
  1663.     var req = browser.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  1664.     var prog = req.getInterface(Components.interfaces.nsIWebProgress);
  1665.     CookiesProgressListener.removeProgressListener(prog);
  1666.   },
  1667.   
  1668.   renameSession : function (src_session_id, dst_session_id) {
  1669.     CookiesStorage.renameSession(src_session_id, dst_session_id);
  1670.   },
  1671.   
  1672.   setSessionInfo : function (session_id, user_name, user_avatar, user_network_name, user_network_id) {
  1673.     CookiesStorage.setSessionInfo(session_id, user_name, user_avatar, user_network_name, user_network_id);
  1674.   },
  1675.   
  1676.   getSessionInfo : function (browser, callback) {
  1677.     if (browser.__cookies && browser.__cookies._sessionId)
  1678.       CookiesStorage.getSessionInfo(browser.__cookies._sessionId, callback);
  1679.     else if (typeof callback=="function")
  1680.       callback();
  1681.   },
  1682.   
  1683.   deleteSessionInfo : function (session_id) {
  1684.     CookiesStorage.deleteSession(session_id);
  1685.   },
  1686.   
  1687.   getSessionId : function (browser) {
  1688.     if (browser && browser.__cookies && browser.__cookies._sessionId!="undefined")
  1689.       return browser.__cookies._sessionId;
  1690.     return null;
  1691.   },
  1692.   
  1693.   getAllSessions : function (callback) {
  1694.     CookiesStorage.getAllSessions(callback);
  1695.   },
  1696.   
  1697.   attachSessionToBrowser : function (browser, session_id, onReadyCallback) {
  1698.     browser.__cookies = new CookiesDB(session_id, onReadyCallback);
  1699.     CookiesService._listener.onBrowserAttached(browser, session_id);
  1700.   },
  1701.   detachSessionFromBrowser : function (browser) {
  1702.     browser.__cookies = null;
  1703.     CookiesService._listener.onBrowserDetached(browser);
  1704.   },
  1705.   
  1706.   addCustomProfile : function (name, home, avatar, callback) {
  1707.     
  1708.     if (!avatar || avatar.length==0) {
  1709.       try {
  1710.         var faviconService = Components.classes["@mozilla.org/browser/favicon-service;1"].getService(Components.interfaces.nsIFaviconService);
  1711.         var IOService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  1712.         var uri = IOService.newURI(home, null, null);
  1713.         //avatar = faviconService.getFaviconForPage(uri).spec;
  1714.         avatar = uri.prePath + "/favicon.ico";
  1715.         Components.utils.reportError(home+" --> "+avatar);
  1716.       } catch(e) {
  1717.         Components.utils.reportError(e);
  1718.       }
  1719.     }
  1720.     var id = "custom_"+new Date().getTime();
  1721.     CookiesStorage.setSessionInfo(id, name, avatar, null, null, home);
  1722.     if (typeof callback=="function")
  1723.       callback(id);
  1724.   }
  1725.   
  1726. };
  1727.